<?php

namespace App\Common\Libs;

use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\GuzzleException;

/**
 * Http 请求类封装
 * @example $res = HttpClient::getInstance()->get($url, $params);
 * @preview: $url = 'https://dweb.tingjunapp.com/dapi/api/message/comment-receive';
 * $data = HttpClient::getInstance()->setHeaders(['uid' => '00KBZUWHQUDCLLVPOYUQJ4UH'])->post($url, ['receive_id' => '22']);
 * response data : {"message":"OK","statusCode":200,"success":true,"result":{"have_more":false,"list":[]}}
 * Class HttpClient
 * @package App\Common\Libs
 */
class HttpClient
{

    /** @var mixed|string $baseUri */
    public $baseUri;

    /** @var $path */
    public $path;

    /** @var $url */
    public $url;

    /** @var mixed|string $cookies */
    public $cookies;

    /** @var mixed|string $headers */
    public $headers;

    /** @var bool $verify */
    public $verify;

    /** @var int $timeout */
    public $timeout;

    /** @var array $options */
    public $options;

    /** @var Client $client */
    public $client;

    /** @var HttpClient $instance */
    protected static $instance;

    /** @var bool $isWrapResult */
    protected $isWrapResult = false;

    /**
     * HttpClient constructor.
     * @param array $options
     */
    public function __construct($options = [])
    {
        $this->baseUri = $options['base_uri'] ?? '';
        $this->cookies = $options['cookies'] ?? '';
        $this->headers = $options['headers'] ?? '';
        $this->timeout = $options['timeout'] ?? 5;
        $this->verify = $options['verify'] ?? false;
        $this->options = $options;
    }

    /**
     * 获取Http请求实例
     * @param array $options
     * @return HttpClient
     */
    public static function getInstance($options = [])
    {
        if (self::$instance instanceof HttpClient) {
            return self::$instance;
        }
        return self::$instance = new self($options);
    }

    /**
     * send get request
     * @param $uri
     * @param array $params
     * @param array $options
     * @return array|mixed
     */
    public function get($uri, $params = [], $options = [])
    {
        $options = array_merge(['query' => $params], $options);
        return $this->request($uri, 'GET', $options);
    }

    /**
     * send post request
     * @param $uri
     * @param array $params
     * @param array $options
     * @return array|mixed
     */
    public function post($uri, $params = [], $options = [])
    {
        $options = array_merge(['json' => $params], $options);
        return $this->request($uri, 'POST', $options);
    }

    /**
     * send put request
     * @param $uri
     * @param array $params
     * @param array $options
     * @return array|mixed
     */
    public function put($uri, $params = [], $options = [])
    {
        $options = array_merge(['json' => $params], $options);
        return $this->request($uri, 'PUT', $options);
    }

    /**
     * send delete request
     * @param $uri
     * @param array $params
     * @param array $options
     * @return array|mixed
     */
    public function delete($uri, $params = [], $options = [])
    {
        $options = array_merge(['json' => $params], $options);
        return $this->request($uri, 'DELETE', $options);
    }

    /**
     * request
     * @param $url
     * @param string $method
     * @param array $options
     * @return array|mixed
     */
    public function request($url, $method = 'GET', $options = [])
    {
        try{
            $config = $this->getConfig($url);
            $res = $this->client($config)
                ->request($method, $url, $options);
            $list = [
                'code' => $res->getStatusCode(),
                'headers' => json_encode($res->getHeaders()),
                'content' => $res->getBody()->getContents(),
                'meta' => json_encode($res->getBody()->getMetadata()),
            ];
        } catch (GuzzleException $e) {
            $list = [
                'code' => $e->getCode(),
                'headers' => $e->getPrevious(),
                'content' => $e->getMessage(),
                'meta' => $e->getFile().':'.$e->getLine(),
            ];
        }
        $result = !$list['content'] ?  (object)[] : (json_decode($list['content'], true) ? : $list['content']);
        if ($this->isWrapResult) {
            return [
                'message' => $list['code'] == 200 ? 'Ok' : 'Client Error',
                'statusCode' => $list['code'],
                'success' =>  $list['code'] == 200 ? true : false,
                'result' => $list
            ];
        }
        return $result;
    }

    /**
     * get client
     * @param array $config
     * @return Client
     */
    public function client($config = [])
    {
        $config = $config ? : $this->options;
        $this->client = new Client($config);
        return $this->client;
    }

    /**
     * set wrap
     * @param $isWrap
     * @return $this
     */
    public function setWrap($isWrap)
    {
        $this->isWrapResult = $isWrap;
        return $this;
    }

    /**
     * set cookies
     * $cookies = [
     *       'PHPSESSID' => 'b68212de1826c64d77b69dc514c2a9cb'
     *       'token'     => '00K5N8D5CZEIB52BQM6Z2HJE',
     *  ];
     * @param array $cookies
     * @return $this
     */
    public function setCookies(array $cookies)
    {
        $this->cookies = $cookies;
        return $this;
    }

    /**
     * set headers
     * $headers = [
     *       'User-Agent' => 'testing/1.0',
     *       'Accept'     => 'application/json',
     *       'X-Foo'      => ['Bar', 'Baz']
     *  ];
     * @param array $headers
     * @return $this
     */
    public function setHeaders(array $headers)
    {
        $this->headers = $headers;
        return $this;
    }

    /**
     * get config
     * @param $url
     * @return array
     */
    public function getConfig($url)
    {
        $config = [];
        if ($this->baseUri) {
            $config['base_uri'] = $this->baseUri;
        }
        if ($this->cookies) {
            // @toU : if you want assigned to root domain, please init baseUrl and url.
            $config['cookies'] = CookieJar::fromArray($this->cookies, $this->baseUri ? : dirname($url));
        }
        if ($this->headers) {
            $config['headers'] = $this->headers;
        }
        if ($this->timeout) {
            $config['timeout'] = $this->timeout;
        }
        if (is_bool($this->verify)) {
            $config['verify'] = $this->verify;
        }
        $config = array_merge($this->options, $config);
        return $config;
    }

}
